package org.budo.weixin.work.api;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.qq.weixin.mp.aes.AesException;
import com.qq.weixin.mp.aes.WXBizMsgCrypt;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import org.budo.support.fastjson.Fastjson;
import org.budo.support.http.client.HttpUtil;
import org.budo.support.http.client.Request;
import org.budo.support.http.client.Response;
import org.budo.support.lang.util.BooleanUtil;
import org.budo.support.lang.util.ListUtil;
import org.budo.support.lang.util.MapUtil;
import org.budo.support.lang.util.StringUtil;
import org.budo.time.Time;
import org.budo.weixin.api.WeixinMediaUploadResult;
import org.budo.weixin.work.TaskCardMessage;
import org.budo.weixin.work.WorkWeixinMessage;
import org.budo.weixin.work.api.entity.ApprovalDetail;
import org.budo.weixin.work.api.entity.ApprovalDetailImpl;
import org.budo.weixin.work.api.entity.ApprovalWebhookImpl;
import org.budo.weixin.work.api.entity.ChatInfo;
import org.budo.weixin.work.api.entity.Department;
import org.budo.weixin.work.api.entity.TemplateContent;
import org.budo.weixin.work.api.entity.TemplateDetail;
import org.budo.weixin.work.api.entity.User;
import org.budo.weixin.work.api.entity.WorkWeixinToken;
import org.budo.weixin.work.message.WorkWeixinMessageDomain;
import org.budo.weixin.work.util.WorkWeixinMessageUtil;

import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
 * @author lmw
 */
@Slf4j
@Getter
@Setter
@Accessors(chain = true)
@NoArgsConstructor
public class WorkWeixinApiImpl extends AbstractWorkWeixinApi {
    private static final long serialVersionUID = -931763861838103113L;

    private static final String CONVERT_TO_USERID = "https://qyapi.weixin.qq.com/cgi-bin/user/convert_to_userid";

    private static final String CONVERT_TO_OPENID = "https://qyapi.weixin.qq.com/cgi-bin/user/convert_to_openid";

    private static final String APPCHAT_GET = "https://qyapi.weixin.qq.com/cgi-bin/appchat/get";

    private static final String APPCHAT_SEND = "https://qyapi.weixin.qq.com/cgi-bin/appchat/send";

    private static final String APPCHAT_UPDATE = "https://qyapi.weixin.qq.com/cgi-bin/appchat/update";

    private static final String APPCHAT_CREATE = "https://qyapi.weixin.qq.com/cgi-bin/appchat/create";

    private static final String USER_CREATE = "https://qyapi.weixin.qq.com/cgi-bin/user/create";

    private static final String GETTOKEN = "https://qyapi.weixin.qq.com/cgi-bin/gettoken";

    private static final String GET_JSAPI_TICKET = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket";

    private static final String TICKET_GET = "https://qyapi.weixin.qq.com/cgi-bin/ticket/get";

    private static final String GETCHECKINOPTION = "https://qyapi.weixin.qq.com/cgi-bin/checkin/getcheckinoption";

    private static final String GETCHECKINDATA = "https://qyapi.weixin.qq.com/cgi-bin/checkin/getcheckindata";

    private static final String MEDIA_UPLOAD = "https://qyapi.weixin.qq.com/cgi-bin/media/upload";

    private static final String MESSAGE_SEND = "https://qyapi.weixin.qq.com/cgi-bin/message/send";

    private static final String MEDIA_GET = "https://qyapi.weixin.qq.com/cgi-bin/media/get";

    private static final String USER_LIST = "https://qyapi.weixin.qq.com/cgi-bin/user/list";

    private static final String DEPARTMENT_LIST = "https://qyapi.weixin.qq.com/cgi-bin/department/list";

    private static final String USER_GET = "https://qyapi.weixin.qq.com/cgi-bin/user/get";

    private static final String MENU_DELETE = "https://qyapi.weixin.qq.com/cgi-bin/menu/delete";

    private static final String MENU_GET = "https://qyapi.weixin.qq.com/cgi-bin/menu/get";

    private static final String MENU_CREATE = "https://qyapi.weixin.qq.com/cgi-bin/menu/create";

    private static final String GETTEMPLATEDETAIL = "https://qyapi.weixin.qq.com/cgi-bin/oa/gettemplatedetail";

    private static final String APPLYEVENT = "https://qyapi.weixin.qq.com/cgi-bin/oa/applyevent";

    private static final String GETAPPROVALINFO = "https://qyapi.weixin.qq.com/cgi-bin/oa/getapprovalinfo";

    private static final String GETAPPROVALDETAIL = "https://qyapi.weixin.qq.com/cgi-bin/oa/getapprovaldetail";

    private static final String GETUSERINFO = "https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo";

    private static final String DELETE_USER = "https://qyapi.weixin.qq.com/cgi-bin/user/delete";

    private static final String UPDATE_USER = "https://qyapi.weixin.qq.com/cgi-bin/user/update";

    public WorkWeixinApiImpl(String accessToken) {
        this.setAccessToken(accessToken);
    }

    public WorkWeixinApiImpl(String corpId, String corpSecret) {
        this.setCorpId(corpId);
        this.setCorpSecret(corpSecret);
    }

    /**
     * @see https://work.weixin.qq.com/api/doc/90000/90135/91983
     */
    @Override
    public ApprovalDetail getApprovalDetail(String sp_no) {
        if (StringUtil.isNullOrEmpty(sp_no)) {
            log.info("#54 getApprovalDetail error, return null, sp_no=" + sp_no);
            return null;
        }

        String requestBody = "{\"sp_no\":" + sp_no + "}";

        String url = GETAPPROVALDETAIL + "?access_token=" + this.accessToken();
        Response response = HttpUtil.post(url, requestBody.getBytes());
        String responseBody = response.getBody();
        if (null == responseBody || responseBody.isEmpty()) {
            log.error("#74 getApprovalDetail, responseBody = " + responseBody + ", requestBody=" + requestBody + ", url=" + url);
            return null;
        }

        return new ApprovalDetailImpl(responseBody);
    }

    /**
     * @see https://work.weixin.qq.com/api/doc/90000/90135/91816
     */
    @Override
    public List<String> getApprovalInfo(Date startTime, Date endTime, Map<String, Object> filters, Integer cursor, Integer size) {
        String url = GETAPPROVALINFO + "?access_token=" + this.accessToken();

        String requestBody = "{\"starttime\":\"" + Time.when(startTime).toSeconds() + "\",\"endtime\":\"" + Time.when(endTime).toSeconds() + "\",\"cursor\":" + cursor + ",\"size\":" + size;
        if (null != filters && !filters.isEmpty()) {
            List<String> filterList = new ArrayList<String>();
            for (Entry<String, Object> entry : filters.entrySet()) { // creator // LiMingWei
                filterList.add("{\"key\":\"" + entry.getKey() + "\",\"value\":\"" + entry.getValue() + "\"}");
            }
            requestBody += ",\"filters\":[" + StringUtil.join(filterList) + "]}";
        }
        requestBody += "}"; //

        Request request = new Request();
        request.setUrl(url);
        request.setRequestBody(requestBody);
        Response response = request.execute();

        log.info("#96 response = " + response + ", request = " + request);

        JSONObject jsonObject = Fastjson.parseObject(response.bodyOrToString());
        JSONArray sp_no_list = jsonObject.getJSONArray("sp_no_list");
        return ListUtil.toStringList_2(sp_no_list);
    }

    @Override
    public String applyEvent(String creatorUserId, String templateId, String summary, Map<String, Object> templateContent) {
        List<String> summaryList = Arrays.asList(summary);
        return this.applyEvent(creatorUserId, templateId, summaryList, templateContent);
    }

    @Override
    public String applyEvent(String creatorUserId, String templateId, List<String> summaryList, Map<String, Object> templateContent) {
        if (StringUtil.isNullOrEmpty(creatorUserId) || StringUtil.isNullOrEmpty(templateId) //
                || ListUtil.isNullOrEmpty(summaryList) || MapUtil.isNullOrEmpty(templateContent)) {
            log.error("#128 applyEvent, creatorUserId=" + creatorUserId + ", templateId=" + templateId //
                    + ", summaryList=" + summaryList + ", templateContent=" + templateContent);
            throw new IllegalStateException("#105 applyEvent args empty, creatorUserId=" + creatorUserId + ", templateId=" + templateId //
                    + ", summaryList=" + summaryList + ", templateContent=" + templateContent);
        }

        TemplateDetail templateDetail = this.getTemplateDetail(templateId);
        if (null == templateDetail || !templateDetail.isOk()) {
            log.error("#136 applyEvent, templateDetail=" + templateDetail + ", templateId=" + templateId);
            throw new IllegalStateException("#111 templateDetail not ok, templateDetail=" + templateDetail + ", templateId=" + templateId);
        }

        // 表单内容
        List<String> contentJsonArray = new ArrayList<String>();

        List<TemplateContent> controls = templateDetail.getTemplateContentList();
        for (TemplateContent control : controls) {
            Object val = control.getValue(templateContent);
            if (null == val && control.getRequire()) { // 必填参数为空 异常
                throw new IllegalArgumentException("#90 control " + control.getTitle() + " is required, templateContent=" + templateContent + ", control=" + control);
            }

            if (null == val) {
                continue; // 非必须参数为空 跳过
            }

            String _withValue = control.withValue(val, this); // 参数包裹
            contentJsonArray.add(_withValue);
        }

        String requestBody = "{" + //
                "    \"creator_userid\":\"" + creatorUserId + "\"," + //
                "    \"template_id\":\"" + templateId + "\"," + //
                "    \"use_template_approver\":1," + // 1 后台配置审批人 0 手动指定审批人
                "    \"apply_data\":{";

        requestBody += " \"contents\":[" + StringUtil.join(contentJsonArray) + "]";
        requestBody += "    },";

        List<String> _summaryList = new ArrayList<String>();

        for (String summary : summaryList) {
            _summaryList.add("" //
                    + "{" //
                    + "    \"summary_info\":[" // 摘要
                    + "        {" //
                    + "            \"text\":\"" + summary + "\"," //
                    + "            \"lang\":\"zh_CN\"" //
                    + "        }" //
                    + "    ]" //
                    + "}");
        }

        requestBody += "\"summary_list\":[" + StringUtil.join(_summaryList) + "    ]";
        requestBody += "}";

        String url = APPLYEVENT + "?access_token=" + this.accessToken();

        Response response = HttpUtil.post(url, requestBody.getBytes());
        String responseBody = response.getBody();

        // 异常返回
        if (null == responseBody || responseBody.trim().isEmpty()) {
            log.error("#122 applyEvent, responseBody = " + responseBody + ", requestBody=" + requestBody + ", url=" + url);
            throw new IllegalStateException("#162 wx no responseBody, responseBody=" + responseBody + ", requestBody=" + requestBody);
        }

        // 异常返回
        JSONObject jsonObject = JSON.parseObject(responseBody);
        String sp_no = jsonObject.getString("sp_no");
        if (StringUtil.isNullOrEmpty(sp_no)) {
            log.error("#129 applyEvent, responseBody = " + responseBody + ", requestBody=" + requestBody + ", url=" + url);
            throw new IllegalStateException("#169 wx no sp_no, responseBody=" + responseBody + ", requestBody=" + requestBody);
        }

        // 正常返回
        log.info("#169 after applyEvent, sp_no=" + sp_no + ", creatorUserId=" + creatorUserId + ", templateId=" + templateId + ", requestBody=" + requestBody);
        return sp_no;
    }

    @Override
    public TemplateDetail getTemplateDetail(String templateId) {
        String url = GETTEMPLATEDETAIL + "?access_token=" + this.accessToken();
        String requestBody = "{\"template_id\":\"" + templateId + "\"}";

        String responseBody = HttpUtil.post(url, requestBody.getBytes()).getBody();
        log.debug("#132 getTemplateDetail, responseBody = " + responseBody + ", requestBody=" + requestBody + ", url=" + url);

        return new TemplateDetail(responseBody);
    }

    @Override
    public String getWeixinToken(String corpId, String corpSecret) {
        WorkWeixinToken token = this.getToken(corpId, corpSecret);
        return null == token ? null : token.getAccessToken();
    }

    @Override
    public String createMenu(String menusToString, String agentId) {
        String url = MENU_CREATE + "?access_token=" + this.accessToken() + "&agentid=" + agentId;

        Request request = new Request();
        request.setUrl(url);
        request.setMethod(Request.POST);
        request.setRequestBody(menusToString.getBytes());

        Response response = request.execute();
        log.info("#72 createMenu, response=" + (null == response ? response : response.toSimpleString()));

        return response.getBody();
    }

    @Override
    public String getMenu(String agentId) {
        String url = MENU_GET + "?access_token=" + this.accessToken() + "&agentid=" + agentId;

        Request request = new Request();
        request.setUrl(url);
        request.setMethod(Request.GET);
        Response response = request.execute();

        log.info("#85 getMenu, response=" + (null == response ? response : response.toSimpleString()));
        return response.getBody();
    }

    @Override
    public String deleteMenu(String agentId) {
        String url = MENU_DELETE + "?access_token=" + this.accessToken() + "&agentid=" + agentId;

        Request request = new Request();
        request.setUrl(url);
        request.setMethod(Request.GET);
        Response response = request.execute();

        log.info("#97 deleteMenu, response=" + (null == response ? response : response.toSimpleString()));
        return response.getBody();
    }

    @Override
    public Map<String, Object> getUserInfo(String userId) {
        String url = USER_GET + "?access_token=" + this.accessToken() + "&userid=" + userId;
        String response = HttpUtil.get(url).getBody();

        if (null == response || response.trim().isEmpty() || response.contains("no privilege to")) {
            throw new RuntimeException("#106 no privilege to getUserInfo error, url=" + url + ", response=" + response);
        }

        log.info("#109 getUserInfo(), url={}, response={}", url, response);
        return JSON.parseObject(response);
    }

    /**
     * @param departmentId
     * @return { "errcode": 0, "errmsg": "ok", "department": [ { "id": 2,
     *         "name":"广州研发中心", "parentid": 1, "order": 10 }, { "id": 3 "name":
     *         "邮箱产品部","parentid": 2, "order": 40 } ] }
     */
    @Override
    public List<Department> getDepartmentList(String departmentId) {
        String url = DEPARTMENT_LIST + "?access_token=" + this.accessToken();
        if (null != departmentId) {
            url += "&id=" + departmentId;
        }

        String responseBody = HttpUtil.get(url).getBody();
        log.info("#146 getDepartmentList(), url={}, response={}", url, responseBody);

        JSONObject jsonObject = JSON.parseObject(responseBody);
        JSONArray jsonArray = jsonObject.getJSONArray("department");
        return jsonArray.toJavaList(Department.class);
    }

    /**
     * 获取部门成员详情
     *
     * @param departmentId 部门ID
     * @param isFetchChild 是否递归获取子部门下面的成员
     * @return https://work.weixin.qq.com/api/doc#10063
     */
    @Override
    public List<User> getUserInfoListByDepartmentId(Integer departmentId, Boolean fetchChild) {
        String _fetchChild = BooleanUtil.isTrue(fetchChild) ? "1" : "0";
        String _url = USER_LIST + "?access_token=%s&department_id=%s&fetch_child=%s";
        String url = String.format(_url, this.accessToken(), departmentId, _fetchChild);

        String responseBody = HttpUtil.get(url).getBody();
        log.info("#164 getUserInfoListByDepartmentId(), url={}, response={}", url, responseBody);

        JSONObject jsonObject = JSON.parseObject(responseBody);
        JSONArray jsonArray = jsonObject.getJSONArray("userlist");

        return jsonArray.toJavaList(User.class);
    }

    /**
     * 微信是跳转
     *
     * @return 返回的是跳转地址
     */
    @Override
    public String getMedia(String mediaId) {
        String url = MEDIA_GET + "?access_token=" + this.accessToken() //
                + "&media_id=" + mediaId;
        return url;
    }

    /**
     * 企业号二维码
     */
    @Override
    public String getWorkWeixinQrCode() {
        throw new RuntimeException();
    }

    /**
     * @see org.budo.weixin.api.WeixinApi#sendMessage(org.budo.open.sdk.weixin.message.Message)
     */
    @Override
    public String sendMessage(WorkWeixinMessage workWeixinMessage) {
        String messageJson = WorkWeixinMessageUtil.toJson(workWeixinMessage);

        return this.send_0(messageJson);
    }

    @Override
    public String sendMessage(WorkWeixinMessageDomain workWeixinMessage) {
        String jsonString = JSON.toJSONString(workWeixinMessage);
        return this.send_0(jsonString);
    }

    @Override
    public String sendTaskCardMessage(TaskCardMessage taskCardMessage) {
        String messageJson = WorkWeixinMessageUtil.toJson(taskCardMessage);

        return this.send_0(messageJson);
    }

    private String send_0(String messageJson) {
        String url = MESSAGE_SEND + "?access_token=" + this.accessToken();

        Request request = new Request();
        request.setUrl(url);
        request.setMethod(Request.POST);
        request.setRequestBody(messageJson.getBytes());
        Response response = request.execute();

        log.info("#265 send_0 sendMessage To:{} Response：{}", url, response);
        return response.getBody();
    }

    @Override
    public String verifyUrl(String signature, String timestamp, String nonce, String echostr) {
        if (StringUtil.isEmpty(signature) || StringUtil.isEmpty(timestamp) //
                || StringUtil.isEmpty(nonce) || StringUtil.isEmpty(echostr)) {
            log.error("#410 verifyUrl error, signature=" + signature + ", timestamp=" + timestamp + ", nonce=" + nonce + ", echostr=" + echostr);
            return null;
        }

        try {
            WXBizMsgCrypt wxBizMsgCrypt = this.wxBizMsgCrypt();
            return wxBizMsgCrypt.VerifyURL(signature, timestamp, nonce, echostr);
        } catch (AesException e) {
            log.error("#411 verifyUrl error, e=" + e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public String decryptMessage(String signature, String timestamp, String nonce, String requestBody) {
        if (null == requestBody || requestBody.trim().isEmpty()) {
            log.error("#493 decryptMessage, requestBody=" + requestBody);
            return requestBody;
        }

        try {
            return this.wxBizMsgCrypt().DecryptMsg(signature, timestamp, nonce, requestBody);
        } catch (AesException e) {
            log.error("#429 decryptMessage error, signature=" + signature + ", timestamp=" + timestamp + ", nonce=" + nonce //
                    + ", approvalToken=" + this.getApprovalToken() + ", approvalEncodingAesKey=" + this.getApprovalEncodingAesKey() + ", corpId=" + this.getCorpId() //
                    + ", requestBody=" + requestBody //
                    + ", e=" + e, e);
            throw new RuntimeException(e);
        }
    }

    private WXBizMsgCrypt wxBizMsgCrypt() throws AesException {
        return new WXBizMsgCrypt(this.getApprovalToken(), //
                this.getApprovalEncodingAesKey(), //
                this.getCorpId());
    }

    @Override
    public ApprovalDetail parseApprovalWebhook(String input) {
        if (null == input || input.trim().isEmpty()) {
            return null;
        }

        return new ApprovalWebhookImpl(input);
    }

    /**
     * https://work.weixin.qq.com/api/doc/90000/90135/90253
     *
     * @see org.budo.weixin.api.WeixinApi#uploadMedia(String, Object)
     */
    @Override
    public WeixinMediaUploadResult uploadMedia(String type, Object media) {
        String url = MEDIA_UPLOAD + "?access_token=" + this.accessToken() //
                + "&type=" + type;

        Request request = new Request();
        request.setMethod(Request.POST);
        request.setUrl(url);
        request.setField("media", media);
        Response response = request.execute();

        WeixinMediaUploadResult uploadResult = new WeixinMediaUploadResult(response.getBody());
        if (!uploadResult.getSuccess()) {
            log.error("#184 uploadMedia failed, response={}, request={}", response, request);
            return uploadResult;
        }

        log.debug("#186 uploadMedia success, response={}, request={}", response, request);
        return uploadResult;
    }

    @Override
    public String uploadFileByUrl(URL url) {
        WeixinMediaUploadResult uploadMedia = this.uploadMedia("file", url);
        return uploadMedia.getMediaId();
    }

    @Override
    public String uploadMediaReturnId(String type, Object media) {
        WeixinMediaUploadResult uploadMedia = this.uploadMedia(type, media);
        return uploadMedia.getMediaId();
    }

    // https://work.weixin.qq.com/api/doc/90000/90135/90262
    @Override
    public void getCheckinData() {
        String url = GETCHECKINDATA + "?access_token=" + this.accessToken();

        Request request = new Request();
        request.setMethod(Request.POST);
        request.setUrl(url);
        String requestBody = "{\n" + "   \"opencheckindatatype\": 3,\n" + "   \"starttime\": 1492617600,\n" + "   \"endtime\": 1492790400,\n" + "   \"useridlist\": [\"limw\",\"luhl\"]\n" + "}";
        request.setRequestBody(requestBody);
        Response response = request.execute();

        log.info("#535 getCheckinData, response=" + response);
    }

    // https://work.weixin.qq.com/api/doc/90000/90135/90263
    @Override
    public void getCheckinOption() {
        String url = GETCHECKINOPTION + "?access_token=" + this.accessToken();

        Request request = new Request();
        request.setMethod(Request.POST);
        request.setUrl(url);
        Response response = request.execute();

        log.info("#535 getCheckinOption, response=" + response);
    }

    // https://work.weixin.qq.com/help?person_id=1&doc_id=13376
    @Override
    public String webhookSendText(String url, String text) {
        Request request = new Request();
        request.setUrl(url);
        request.setContentType("Content-Type: application/json");
        request.setRequestBody("{\"msgtype\":\"text\",\"text\":" + Fastjson.toJSONString(MapUtil.stringObjectMap("content", text)) + "}");
        Response response = request.execute();

        log.info("#537 webhookSendText, response=" + response);
        return response.bodyOrToString();
    }

    @Override
    public String getTicket(String accessToken, String type) {
        String url = TICKET_GET + "?access_token=" + accessToken //
                + "&type=" + type;

        Request request = new Request();
        request.setMethod(Request.GET);
        request.setUrl(url);
        request.setContentType("Content-Type: application/json");
        Response response = request.execute();
        log.info("#539 ticket, response=" + response);
        if (response == null) {
            return null;
        }
        String responseBody = response.getBody();
        JSONObject responseMap = JSON.parseObject(responseBody);
        return StringUtil.toString(responseMap.get("ticket"));
    }

    @Override
    public String getJsapiTicket(String access_token, String type) {
        String url = GET_JSAPI_TICKET + "?access_token=" + access_token;
        if (null != type) {
            url += "&type=" + type;
        }

        Request request = new Request();
        request.setMethod(Request.GET);
        request.setUrl(url);
        request.setContentType("Content-Type: application/json");
        Response response = request.execute();
        log.info("#540 jsapi ticket, response=" + response);

        if (response == null) {
            return null;
        }

        String responseBody = response.getBody();
        JSONObject responseMap = JSON.parseObject(responseBody);
        return StringUtil.toString(responseMap.get("ticket"));
    }

    @Override
    public String getJsapiTicketNoType(String access_token) {
        return this.getJsapiTicket(access_token, null);
    }

    @Override
    public WorkWeixinToken getToken(String corpId, String corpSecret) {
        if (StringUtil.isNullOrEmpty(corpId) || StringUtil.isNullOrEmpty(corpSecret)) {
            throw new IllegalArgumentException("#158 getToken error, corpId=" + corpId + ", corpSecret=" + corpSecret);
        }

        String url = GETTOKEN + "?corpid=" + corpId + "&corpsecret=" + corpSecret;

        Request request = new Request();
        request.setUrl(url);
        request.setMethod(Request.GET);
        Response response = request.execute();
        log.info("#231 getToken, response=" + (null == response ? response : response.toSimpleString()));

        String responseBody = response.getBody();
        JSONObject responseMap = JSON.parseObject(responseBody);

        String access_token = StringUtil.toString(responseMap.get("access_token"));
        Integer expires_in = responseMap.getInteger("expires_in");

        if (StringUtil.isEmpty(access_token)) {
            log.error("#55 获取微信Token失败, corpId={}, corpSecret={}, response={}", corpId, corpSecret, response);
        }

        WorkWeixinToken workWeixinToken = new WorkWeixinToken(access_token, expires_in);
        this.setWorkWeixinToken(workWeixinToken);

        return workWeixinToken;
    }

    @Override
    public String getAccessToken(String corpid, String corpsecret) {
        WorkWeixinToken token = this.getToken(corpid, corpsecret);
        return null == token ? null : token.getAccessToken();
    }

    @Override
    public String createUser(String userid, String name, List<Integer> department, String mobile) {
        if (null == userid || userid.trim().isEmpty() || null == name || name.trim().isEmpty() || null == department || department.isEmpty() || mobile == null || mobile.trim().isEmpty()) {
            log.error("#69 createUser error, userid=" + userid + ", name=" + name + ", department=" + department + ", mobile=" + mobile);
            return null;
        }

        String requestBody = "{\"userid\":\"" + userid + "\",\"name\":\"" + name + "\",\"department\":[" + StringUtil.join(department) + "],\"mobile\":\""+mobile+"\"}";

        String url = USER_CREATE + "?access_token=" + this.accessToken();
        Response response = HttpUtil.post(url, requestBody.getBytes());
        String responseBody = response.getBody();
        if (null == responseBody || responseBody.isEmpty()) {
            log.error("#74 createUser, responseBody = " + responseBody + ", requestBody=" + requestBody + ", url=" + url);
            return null;
        }

        log.info("#83 createUser, response=" + response);
        return responseBody;
    }

    @Override
    public String deleteUser(String userid) {
        String url = DELETE_USER + "?access_token=" + this.accessToken() + "&userid=" + userid;
        Request request = new Request();
        request.setUrl(url);
        request.setMethod(Request.GET);
        Response response = request.execute();
        log.info("#673 delete user, response=" + (null == response ? response : response.toSimpleString()));
        String responseBody = response.getBody();
        return responseBody;
    }

    @Override
    public String updateDepartment(String userid, List<Integer> department) {
        if (null == userid || (department == null || department.size() == 0)) {
            log.error("#681 update department error, userid=" + userid + ",  department=" + department);
            return null;
        }
        String requestBody = "{\"userid\":\"" + userid + "\",\"department\":[" + StringUtil.join(department) + "]}";

        String url = UPDATE_USER + "?access_token=" + this.accessToken();
        Response response = HttpUtil.post(url, requestBody.getBytes());
        String responseBody = response.getBody();
        if (null == responseBody || responseBody.isEmpty()) {
            log.error("#690 update department, responseBody = " + responseBody + ", requestBody=" + requestBody + ", url=" + url);
            return null;
        }

        log.info("#694 update department, response=" + response);
        return responseBody;
    }

    @Override
    public String getOpenIdByCode(String accessToken, String code) {
        Map<String, Object> map = this.getUserInfo(accessToken, code);
        return map.get("OpenId") + "";
    }

    @Override
    public String getUserIdByCode(String accessToken, String code) {
        Map<String, Object> map = this.getUserInfo(accessToken, code);
        return map.get("UserId") + "";
    }

    // {"DeviceId":"84ba2ef5-3d48-4538-9270-d6c6ea294bbe","errcode":0,"errmsg":"ok","OpenId":"oZviKwBIVqDda-Q4yBPeU_f8l07E"}
    @Override
    public String getUserIdOrOpenIdByCode(String accessToken, String code) {
        Map<String, Object> map = this.getUserInfo(accessToken, code);

        String userId = map.get("UserId") + "";
        if (null != userId && !userId.trim().isEmpty() && !"null".equals(userId.trim())) {
            return userId;
        }

        String openId = map.get("OpenId") + "";
        if (null == openId || openId.trim().isEmpty() || "null".equals(openId.trim())) {
            log.error("#349 getUserIdOrOpenIdByCode, map=" + map + ", accessToken=" + accessToken + ", code=" + code);
        }

        return openId;
    }

    @Override
    public Map<String, Object> getSnsToken(String appId, String appSecret, String code) {
        String access_token = this.accessToken();
        return this.getUserInfo(access_token, code);
    }

    private Map<String, Object> getUserInfo(String access_token, String code) {
        String url = GETUSERINFO + "?access_token=" + access_token + "&code=" + code;

        Request request = new Request();
        request.setUrl(url);
        request.setMethod(Request.GET);
        Response response = request.execute();

        log.info("#695 getUserInfo, response=" + (null == response ? response : response.toSimpleString()));

        String responseBody = response.getBody();
        Map<String, Object> responseMap = Fastjson.parseObject(responseBody);
        return responseMap;
    }

    @Override
    public String createAppChat(String access_token, String name, String owner, List<String> userlist) {
        String url = APPCHAT_CREATE + "?access_token=" + access_token;

        Map<String, Object> map = MapUtil.stringObjectMap("name", name, "owner", owner, "userlist", userlist);

        Request request = new Request();
        request.setUrl(url);
        request.setMethod(Request.POST);
        request.setRequestBody(Fastjson.toJSONString(map));
        Response response = request.execute();

        log.info("#709 createAppChat, response=" + (null == response ? response : response.toSimpleString()));

        String responseBody = response.getBody();
        JSONObject responseMap = Fastjson.parseObject(responseBody);
        return responseMap.getString("chatid");
    }

    @Override
    public String updateAppChat(String access_token, String chatid, String name, String owner, List<String> add_user_list, List<String> del_user_list) {
        String url = APPCHAT_UPDATE + "?access_token=" + access_token;

        Map<String, Object> map = MapUtil.stringObjectMap("chatid", chatid, "name", name, "owner", owner, "add_user_list", add_user_list, "del_user_list", del_user_list);

        Request request = new Request();
        request.setUrl(url);
        request.setMethod(Request.POST);
        request.setRequestBody(Fastjson.toJSONString(map));
        Response response = request.execute();

        log.info("#734 updateAppChat, response=" + (null == response ? response : response.toSimpleString()));

        String responseBody = response.getBody();
        return responseBody;
    }

    @Override
    public ChatInfo getAppChat(String access_token, String chatid) {
        String url = APPCHAT_GET + "?access_token=" + access_token + "&chatid=" + chatid;

        Request request = new Request();
        request.setUrl(url);
        Response response = request.execute();

        log.info("#752 getAppChat, response=" + (null == response ? response : response.toSimpleString()));

        String responseBody = response.getBody();
        return Fastjson.parseObject(responseBody, ChatInfo.class);
    }

    @Override
    public String appChatSend(String access_token, String chatid, String content){
        String url = APPCHAT_SEND + "?access_token=" + access_token;
        Map<String,Object> text = MapUtil.stringObjectMap("content", content);
        Map<String, Object> map = MapUtil.stringObjectMap("chatid", chatid, "msgtype", "text", "text", text);

        Request request = new Request();
        request.setUrl(url);
        request.setMethod(Request.POST);
        request.setRequestBody(Fastjson.toJSONString(map));
        Response response = request.execute();

        log.info("#776 appChatSend, response=" + (null == response ? response : response.toSimpleString()));

        String responseBody = response.getBody();
        return responseBody;
    }

    @Override
    public String convertToOpenid(String access_token, String userid) {
        String url = CONVERT_TO_OPENID + "?access_token=" + access_token;

        Request request = new Request();
        request.setUrl(url);
        request.setMethod(Request.POST);
        request.setRequestBody("{\"userid\":\"" + userid + "\"}");
        Response response = request.execute();

        log.info("#768 convertToOpenid, response=" + (null == response ? response : response.toSimpleString()));

        String responseBody = response.getBody();
        JSONObject parseObject = Fastjson.parseObject(responseBody);
        return parseObject.getString("openid");
    }

    @Override
    public String convertToUserid(String access_token, String openid) {
        String url = CONVERT_TO_USERID + "?access_token=" + access_token;

        Request request = new Request();
        request.setUrl(url);
        request.setMethod(Request.POST);
        request.setRequestBody("{\"openid\":\"" + openid + "\"}");
        Response response = request.execute();

        log.info("#785 convertToUserid, response=" + (null == response ? response : response.toSimpleString()));

        String responseBody = response.getBody();
        JSONObject parseObject = Fastjson.parseObject(responseBody);
        return parseObject.getString("userid");
    }
}
